﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

using Palsta.Core.exp;
using Palsta.Core.view;

namespace Palsta.Core.parser
{
    class ViewParser
    {
        private String context = "";
        public View Parse(String path)
        {
            XmlDocument xd = new XmlDocument();
            xd.Load(path);
            View view = new View();
            for (int index = 0; index < xd.ChildNodes.Count; index++)
            {
                XmlNode node = xd.ChildNodes[index];
                if (node.Name.StartsWith("?"))
                {
                    continue;
                }
                else if (node.Name.Equals("xsl:stylesheet"))
                {
                    for (int i = 0; i < node.ChildNodes.Count; i++)
                    {
                        XmlNode n = node.ChildNodes[i];
                        if (n.Name.Equals("xsl:template"))
                        {
                            String match = n.Attributes["match"].Value;
                            if (match == null || match.Length == 0)
                            {
                                continue;
                            }
                            this.context = match;
                            DomNode templateNode = this.ParseSubNode(n);
                            if (templateNode == null)
                            {
                                continue;
                            }
                            view.Templates.Add(new Template(templateNode, match));
                        }
                    }
                }
            }
            return view;
        }

        private DomNode ParseSubNode(XmlNode node)
        {
            DomGeneralNode domNode = new DomGeneralNode();
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.StartsWith("xsl:"))
                {
                    DomNode dn = null;
                    if (n.Name.Equals("xsl:choose"))
                    {
                        dn = this.ParseXslChoose(n);
                    }
                    else if (n.Name.Equals("xsl:if"))
                    {
                        dn = this.ParseXslIf(n);
                    }
                    else if (n.Name.Equals("xsl:attribute"))
                    {
                        dn = this.ParseXslAttribute(n);
                    }
                    else if (n.Name.Equals("xsl:value-of"))
                    {
                        dn = this.ParseXslVauleOf(n);
                    }
                    if (dn != null)
                    {
                        domNode.SubNodes.Add(dn);
                    }
                }
                else
                {
                    DomNode dn = null;
                    if (n.Name.Equals("#text"))
                    {
                        dn = new DomTextNode();
                        ((DomTextNode)dn).Text = n.Value;
                    }
                    else if (n.Name.Equals("span") && "PlainText".Equals(InfoPathXml.GetNodeAttr(n, "xd:xctname")))
                    {
                        XmlNode plainTextNode = n.OwnerDocument.CreateNode("element", "input", "");
                        XmlNode plainTextValueNode = n.OwnerDocument.CreateNode("element", "xsl:attribute", "");
                        
                        InfoPathXml.SetNodeAttr(plainTextValueNode, "name", "value");
                        while (n.ChildNodes.Count > 0) 
                        {
                            if (n.ChildNodes[0].Name.Equals("xsl:attribute"))
                            {
                                String attrName = InfoPathXml.GetNodeAttr(n.ChildNodes[0], "name");
                                if (attrName == null || attrName.Equals("xd:value"))
                                {
                                    n.RemoveChild(n.ChildNodes[0]);
                                }
                                else
                                {
                                    plainTextNode.AppendChild(n.ChildNodes[0]);
                                }
                            }
                            else if (n.ChildNodes[0].Name.Equals("xsl:value-of"))
                            {
                                n.RemoveChild(n.ChildNodes[0]);
                            }
                            else
                            {
                                plainTextValueNode.AppendChild(n.ChildNodes[0]);
                            }
                        }
                        InfoPathXml.MoveNodeAttrs(n, plainTextNode);
                        if (plainTextValueNode.ChildNodes.Count > 0)
                        {
                            plainTextNode.AppendChild(plainTextValueNode);
                        }

                        n.ParentNode.ReplaceChild(plainTextNode, n);

                        dn = new DomHtmlNode();
                        ((DomHtmlNode)dn).Html = plainTextNode;
                        DomNode sub = this.ParseSubNode(plainTextNode);
                        if (sub == null)
                        {
                            continue;
                        }
                        ((DomHtmlNode)dn).SubNode = sub;
                    }
                    else
                    {
                        dn = new DomHtmlNode();
                        ((DomHtmlNode)dn).Html = n;
                        DomNode sub = this.ParseSubNode(n);
                        if (sub == null)
                        {
                            continue;
                        }
                        ((DomHtmlNode)dn).SubNode = sub;
                    }
                    if (dn != null)
                    {
                        domNode.SubNodes.Add(dn);
                    }
                }
            }
            if (domNode.SubNodes.Count == 1)
            {
                return (DomNode)domNode.SubNodes[0];
            }
            else
            {
                return domNode;
            }
        }

        private DomSetAttributeNode ParseXslAttribute(XmlNode node)
        {
            DomSetAttributeNode domSetAttr = new DomSetAttributeNode();
            String attrName = node.Attributes["name"].Value;
            if (attrName == null || attrName.Length == 0)
            {
                return null;
            }
            domSetAttr.AttrName = attrName;

            DomNode inner = this.ParseSubNode(node);
            if (inner != null)
            {
                domSetAttr.ValueNode = inner;
            }
            return domSetAttr;
        }

        private DomVarNode ParseXslVauleOf(XmlNode node)
        {
            DomVarNode domVar = new DomVarNode();
            String varName = node.Attributes["select"].Value;
            if (varName == null || varName.Length == 0)
            {
                return null;
            }
            domVar.VarName = varName;
            return domVar;
        }

        private DomConditionNode ParseXslChoose(XmlNode node)
        {
            DomConditionNode domCondition = null;
            DomConditionNode current = null;
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                XmlNode n = node.ChildNodes[i];
                if (n.Name.Equals("xsl:when"))
                {
                    DomConditionNode newCondition = new DomConditionNode();
                    String test = n.Attributes["test"].Value;
                    if (test == null || test.Length == 0)
                    {
                        continue;
                    }
                    ExpressionParser parser = new ExpressionParser(test);
                    Expression exp = parser.Parse();
                    if (exp == null)
                    {
                        continue;
                    }
                    newCondition.Exp = exp;
                    DomNode branch = this.ParseSubNode(n);
                    newCondition.TrueNode = branch;
                    newCondition.FalseNode = null;
                    if (current != null)
                    {
                        current.FalseNode = newCondition;
                    }
                    current = newCondition;
                    if (domCondition == null)
                    {
                        domCondition = current;
                    }
                }
                else if (n.Name.Equals("xsl:otherwise"))
                {
                    if (current == null)
                    {
                        continue;
                    }
                    DomNode branch = this.ParseSubNode(n);
                    current.FalseNode = branch;
                }
            }
            return domCondition;
        }

        private DomConditionNode ParseXslIf(XmlNode node)
        {
            DomConditionNode domCondition = new DomConditionNode();
            String test = InfoPathXml.GetNodeAttr(node, "test");
            if (test == null || test.Length == 0)
            {
                return null;
            }
            ExpressionParser parser = new ExpressionParser(test, this.context);
            Expression exp = parser.Parse();
            if (exp == null)
            {
                return null;
            }
            domCondition.Exp = exp;

            DomNode sub = this.ParseSubNode(node);
            if (sub == null)
            {
                return null;
            }
            domCondition.TrueNode = sub;

            return domCondition;
        }
    }
}
